<?php

/*
 * deployer.php created on 24 f�vrier. 08 
 * Author: Rodrigue Le Gall
 *
 * RPortal is an open source web based Content Management System
 * Copyright (C) 2002-2008  Rodrigue Le Gall
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
RPortal :: management();

/**
 * Comment used to start a phpdoc
 */
define( 'START_DOC' , '/**' );

/**
 * Comment used to end a phpdoc
 */
define( 'END_DOC' , '*/' );

/**
 * Comment used to indicate a tag of phpdoc
 */
define( 'TAG_DOC' , '@' );

/**
 * interface to define a deployer
 * 
 * @RPInterface
 */
interface IRPDeploy {
	/**
	 * set the artefact to deploy
	 */
	function setArtefact($artefact);
	/**
	 * deploy the artefact
	 */
	function deploy();
}


/**
 * The deployer will scan a class definition to deploy all components includes in it.
 * 
 * @RPComponent
 */
class FileDeployer implements IRPDeploy {

	private $filePath = "";

	/**
	 * set the classname to deploy
	 */
	public function setArtefact($artefact) {
		$this->filePath = realpath($artefact);
	}

	/**
	 * Deploy the desired class
	 */
	public function deploy() {
		__trace("Deploy file {$this->filePath}");
		$crc = RPORTALM::file_crc($this->filePath);
		__trace(  "It's crc : $crc");
		if(!defined('ALLWAYS_DEPLOY')){
			if($checksum = RPORTALM::getFileChecksum($this->filePath)){
				if($crc==$checksum) {
					__trace("This file already deployed and will not be redeployed");
					return;
				}
			}
		}
		
		$lastComponent = '';
		$annotations = $this->getFileAnnotations($this->filePath);
		RPORTALM :: startTransaction();
		foreach($annotations as $key => $tags){
			if(array_key_exists("@RPInterface",$tags)){
				__trace("## I will treat a @RPInterface");
				//Search interface name from $key
				$name = trim(substr($key,stripos($key,"interface")+9));
				if(strpos($name,' ')===false){
					if(strpos($name,'{')!==false){
						$name = substr($name,0,strpos($name,'{'));
					}
				}else{
					$name = substr($name,0,strpos($name,' '));
				}
				__trace(  "Interface : $name");
				$version = 1;
				//Search if the interface is already deploy
				if($interface = RPORTALM :: getInterface($name)){
					$version = $interface['version'] + 1;
				}
				__trace( "Version : $version");
				RPORTALM :: addInterface($name,$this->filePath,$version);
				__trace("Interface deployed");
			} else if(array_key_exists("@RPComponent",$tags)){
				__trace("## I will treat a @RPComponent");
				//Search component name
				$name = trim(substr($key,stripos($key,"class")+5));
				if(strpos($name,' ')===false){ 
					if(strpos($name,'{')!==false){
						$name = substr($name,0,strpos($name,'{'));
					}
				}else{
					$name = substr($name,0,strpos($name,' '));
				}
				__trace("Component : $name");
				$version = 1;
				if($component = RPORTALM::getComponent($name)){
					$version = $component['version']+1;
				}
				__trace("Version : $version");
				RPORTALM :: addComponent($name,$this->filePath,$version);
				$lastComponent = $name;
				__trace("Component deployed");
				//Search the implemented interface(s)
				$implements = '';
				if(stripos($key,"implements")!==false){
					$implements = trim(substr($key,stripos($key,"implements")+10));
				}
				while($implements!=""){
					$interface='';
					if(strpos($implements,',')!==false){
						$interface = trim(substr($implements,0,strpos($implements,',')));
						$implements=trim(substr($implements,strpos($implements,',')+1));
					} else if(strpos($implements,'{')!==false){
						$interface = trim(substr($implements,0,strpos($implements,'{')));
						$implements = '';
					}else if(strpos($implements,' ')!==false){
						$interface = trim(substr($implements,0,strpos($implements,' ')));
						$implements = '';
					}else{
						$interface = $implements;
						$implements = '';
					}
					__trace("Implements : $interface");
					RPORTALM :: addImplementation($name,$interface);
					__trace("Implementation deployed");
				}
			} else if(array_key_exists("@RPFunction",$tags)){
				//Search for function name
				__trace("## I will treat a @RPFunction");
				$method = trim(substr($key,stripos($key,"function")+8));
				$method = trim(substr($method,0,strpos($method,'(')));
				__trace(  "Function : $method");
				$name = $tags['@RPFunction'][0];
				$name = trim(substr($name,strpos($name,"=")+1));
				__trace("Name : $name");
				$params = array();
				if(array_key_exists("@RPParam",$tags)){
					for($i=0;$i<count($tags);$i++){
						$conf = split(',',$tags['@RPParam'][$i]);
						$param=array();
						for($j=0;$j<count($conf);$j++){
							$value = split('=',$conf[$j]);
							$param['name']='';$param['required']='no';$param['type']='string';$param['method']='ALL';
							switch(trim($value[0])){
								case 'name' : $param['name'] = trim($value[1]);
								case 'required' : $param['required'] = trim($value[1]);
								case 'type' : $param['type'] = trim($value[1]);
								case 'method' : $param['method'] = trim($value[1]);
							}
							$params[]=$param;
						}
					}
				}
				RPORTALM :: addFunction($name,$lastComponent,$method,$params);
			} else if(array_key_exists("@RPValidator",$tags)){
				__trace("## I will treat a @RPValidator");
				$method = trim(substr($key,stripos($key,"function")+8));
				$method = trim(substr($method,0,strpos($method,'(')));
				__trace(  "Function : $method");
				$name = $tags['@RPValidator'][0];
				$name = trim(substr($name,strpos($name,"=")+1));
				__trace("Type : $name");
				__trace("component : $lastComponent");
				RPORTALM :: addValidator($name,$lastComponent,$method);
			}
		}
		RPORTALM :: commit();
	}
	

	/**
	 * get the notations from some php file.
	 */
	private function getFileAnnotations( $strFile )
	{
	    $strText = file_get_contents( $strFile );
	    $arrText = explode( "\n" , $strText );
	   
	    $arrNotations = array();
	   
	    for ( $intCount = 0 ; $intCount < count( $arrText ) ; ++$intCount )
	    {
	        $strLine = $arrText[ $intCount ];
	       
	        // inside the phpdoc //
	        if ( strpos( trim( $strLine ) , START_DOC ) === 0 )
	        {
	            ++$intCount;
	            $strLine = $arrText[ $intCount ];
	            $arrNotation = array();
	           
	            // while the phpdoc is not finished //
	            while (( strpos( trim( $strLine ) , END_DOC ) !== 0 ) and ( $intCount < count( $arrText ) ) )
	            {
	                if(strpos(trim($strLine),TAG_DOC)!==false){
		                // removing the tag doc from the line //
		                $strLine = substr( $strLine , strpos( $strLine , TAG_DOC ) );
		                $strName='';
		                // get the name of the tag //
		                if(strpos(trim($strLine),' ')===false){
		                	$strName=trim($strLine);
		                	$strLine='';
		                }else{
		                	$strName = substr( $strLine , 0 , strpos( $strLine , ' ' ) );
		                	// get the value of the tag //
			                $strLine = substr( $strLine , strpos( $strLine , ' ' ) + 1 );
			               
			                if ( strpos( trim( $strLine ) , '*' ) === 0 )
			                {
			                    $strLine = substr( $strLine , strpos( $strLine , '*' ) + 1 );
			                }
			               
			                $strLine = trim( $strLine );
		                }
		                
		                //$arrNotation[] = array('tag' => $strName,'value'=>trim( $strLine ));
		                if(strpos($strName,'@RP')!==false){
		                	$arrNotation[$strName][] = trim( $strLine );
		                }
	                }
	                ++$intCount;
	                $strLine = $arrText[ $intCount ];
	            }
	            if ( $intCount < count( $arrText ) )
	            {
	                do
	                {
	                    ++$intCount;
	                    $strLine = $arrText[ $intCount ];
	                }
	                while ( $strLine == '' );
	                // adding the notation to the next command line //
	                if(count($arrNotation)>0){
	                	$arrNotations[ trim( $arrText[ $intCount ] ) ] = $arrNotation;
	                }
	                $intCount--;
	            }
	        }
	    }
	    return( $arrNotations );
	}
}

/**
 * This class is responsible to deploy all files in a folder and its sub-folders
 * 
 * @RPComponent
 */
class FolderDeployer extends Component implements IRPDeploy {
	
	private $folder;
	
	/**
	 * set the folder to deploy
	 */
	public function setArtefact($artefact){
		$this->folder = realpath($artefact);
	}
	/**
	 * deploy the folder
	 */
	public function deploy(){
		$this->deployFolder($this->folder);
	}
	
	private function deployFolder($path){
		$d = dir($path);
		while (false !== ($entry = $d->read())) {
			if($entry!='.' && $entry!='..'){
				$currentPath = $path.'/'.$entry;
				__trace(  $currentPath );
				if(is_dir($currentPath)){
					$this->deployFolder($currentPath);
				} else {
					$deployer = new FileDeployer();
					$deployer->setArtefact($currentPath);
					$deployer->deploy();
				}
		   		
			}
		}
		$d->close();
	}
}